Let’s import at all of the packages and the data (RS and Geographic):

library(tidyverse)
library(gridExtra)
library(maps)
library(ggthemes)
library(ggforce)

DeltasClean <- read_csv("../data/out/deltas_clean_v2.csv") 
Parsed with column specification:
cols(
  Delta = col_character(),
  location = col_character(),
  surface = col_character(),
  year = col_double(),
  month = col_double(),
  ndvi = col_double(),
  red = col_double(),
  evi = col_double(),
  savi = col_double(),
  gr = col_double(),
  ndssi = col_double()
)
DeltaLocations <- read_csv("../data/DeltaLocations.csv")
Parsed with column specification:
cols(
  Deltas = col_character(),
  Lat = col_double(),
  Lon = col_double()
)

As a reminder, for each of the 47 deltas there are measurements of Land & Water areas at Upstream, Downstream and ‘Middle’ locations on the delta. We first lump all the observations together, and look to see which Deltas have many observations:

#counts per delta
count(DeltasClean, Delta)
NA

Now, by each month.. where the colorbar represents the number of observations (n) for each month for a given delta:

ggplot(count(DeltasClean, Delta, month), 
       aes(y = Delta, x = month, fill=n)) + 
  geom_tile() + 
  scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
  expand_limits(x = c(1,12)) + 
  scale_fill_gradient( trans = 'log' )

In the above heat map, dark colors (and no color) represent data paucity (and data gaps). Deltas with light colors (e.g., the Parana, Nile, Ebro, Colorado, Brahmani) have lots of data, spread out through the months of the year.

I’ll remove/subset the deltas with sparse coverage (specifically, months with no coverage)….


# need 10 data points per month for NDSSI and NDVI
EnoughObsPerMonth <- DeltasClean %>% ungroup() %>%
  count(Delta, month, surface) %>% 
  group_by(surface) %>%
  filter( n >= 5)

#find deltas missing a given month of observations
DeltaMonthCounts <- EnoughObsPerMonth %>%
  ungroup() %>%
  count(Delta)

# need 12 months of water and land obs, so 24 mo total
EnoughMonths <- DeltaMonthCounts %>%
 filter( n == 24)

CompleteObsDeltas <- pull(EnoughMonths, Delta)

#remove them
DeltasCleaner <- DeltasClean %>%
  filter(Delta %in% CompleteObsDeltas)

#add the real dates in month date format
DeltasCleaner$date <- as.Date(paste(DeltasCleaner$year, DeltasCleaner$month, "01", sep="-"), "%Y-%m-%d")

#remove intermediate data
rm(CompleteObsDeltas, EnoughMonths, EnoughObsPerMonth, DeltaMonthCounts, DeltasClean)

Ok, now let’s organize this raw data into 1 dataframe, with delta, max and min NDVI (and month), max and min NDSSI(and month), NDVI and NDSSI range.

#take the mean NDVI and NDSSI for each month, for each delta
DeltaMeans <- DeltasCleaner %>%
  group_by(Delta, month, surface) %>%
  summarize(MeanNDVI = mean(ndvi, na.rm = TRUE), MeanNDSSI = mean(ndssi, na.rm = TRUE))

#make a 9 column data frame with:
#delta, 
#max and min NDVI month, 
#NDSSI max and min month, 
#max and min values for both NDVI and NDSSI

#####

DeltaMaxNDVI <- 
  DeltaMeans %>% 
  filter(surface == 'Land')  %>% 
  select (-c(MeanNDSSI)) %>% 
  group_by(Delta) %>% 
  slice(which.max(MeanNDVI)) %>% 
  rename(MaxMeanNDVImonth = month, MaxMeanNDVI = MeanNDVI)

DeltaMaxNDSSI <- 
  DeltaMeans %>% 
  filter(surface == 'Water')  %>% 
  select (-c(MeanNDVI)) %>% 
  group_by(Delta) %>% 
  slice(which.max(MeanNDSSI)) %>% 
  rename(MaxMeanNDSSImonth = month, MaxMeanNDSSI = MeanNDSSI)

DeltaMinNDVI <- 
  DeltaMeans %>% 
  filter(surface == 'Land')  %>% 
  select (-c(MeanNDSSI)) %>% 
  group_by(Delta) %>% 
  slice(which.min(MeanNDVI)) %>% 
  rename(MinMeanNDVImonth = month, MinMeanNDVI = MeanNDVI)

DeltaMinNDSSI <- 
  DeltaMeans %>% 
  filter(surface == 'Water')  %>% 
  select (-c(MeanNDVI)) %>% 
  group_by(Delta) %>% 
  slice(which.min(MeanNDSSI)) %>% 
  rename(MinMeanNDSSImonth = month, MinMeanNDSSI = MeanNDSSI)


#join into 1 dataframe
DeltaMaxMin <- left_join(DeltaMaxNDVI, DeltaMaxNDSSI, by = 'Delta') %>% 
  left_join(.,DeltaMinNDVI, by = 'Delta') %>% 
  left_join(.,DeltaMinNDSSI, by = 'Delta') 

#remove intermediate data
rm(DeltaMaxNDVI, DeltaMaxNDSSI, DeltaMinNDSSI,DeltaMinNDVI)

DeltaMaxMin <- DeltaMaxMin %>%
  select(-surface.x, -surface.x.x, -surface.y, -surface.y.y)

And now we calculate phase shifts between NDVI and NDSSI for each delta

#compare offset
DeltaMaxMin <- mutate(DeltaMaxMin, 
                      MinOffset = if_else(MinMeanNDVImonth > MinMeanNDSSImonth, 
                                          MinMeanNDVImonth - MinMeanNDSSImonth,
                                          MinMeanNDSSImonth - MinMeanNDVImonth),
                      MaxOffset = if_else(MaxMeanNDVImonth > MaxMeanNDSSImonth, 
                                          MaxMeanNDVImonth - MaxMeanNDSSImonth,
                                          MaxMeanNDSSImonth - MaxMeanNDVImonth),
                      OffsetDiff = abs(MaxOffset - MinOffset),
                      rangeNDVI = (MaxMeanNDVI - MinMeanNDVI), 
                      rangeNDSSI = (MaxMeanNDSSI - MinMeanNDSSI)
                      )

DeltaMaxMin <- DeltaMaxMin %>% 
  mutate(MinOffset = ifelse(MinOffset > 6, (-1*MinOffset)+12 ,MinOffset))

DeltaMaxMin <- DeltaMaxMin %>% 
  mutate(MaxOffset = ifelse(MaxOffset > 6, (-1*MaxOffset)+12 ,MaxOffset))        
  


DeltaMaxMin

ggplot(DeltaMaxMin, aes(y = Delta, x = MaxOffset)) + geom_point() + 
  scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
  expand_limits(x = c(0,6))  + 
  ggtitle("MaxOffset")


ggplot(DeltaMaxMin, aes(y = Delta, x = MinOffset)) + geom_point() + 
  scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
  expand_limits(x = c(0,6))  + 
  ggtitle("MinOffset")


ggplot(DeltaMaxMin, aes(y = Delta, x = OffsetDiff)) + geom_point() + 
  scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
  expand_limits(x = c(0,6))  + 
  ggtitle("Offset Difference")

Now let’s examine the histograms of all 31 deltas… The months with the greatest mean NDVI, months with gretaest mean NDSSI, the monthly offset, and the skew of the NDSSI and NDVI timeseries.

ggplot(DeltaMaxMin, aes(x = MaxMeanNDVImonth)) + 
  geom_bar(fill=3) + 
  scale_y_continuous(NULL, breaks = NULL) + 
  scale_x_discrete(limits = c(1:12), breaks = c(1:12)) + 
  labs(x = "Month") +
  ggtitle("Month of maximum mean NDVI")
There were 22 warnings (use warnings() to see them)

ggplot(DeltaMaxMin, aes(x = MaxMeanNDSSImonth)) + 
  geom_bar() + 
  scale_y_continuous(NULL, breaks = NULL) + 
  scale_x_discrete(limits = c(1:12), breaks = c(1:12)) + 
  labs(x = "Month") +
  ggtitle("Month of maximum mean NDSSI")


ggplot(DeltaMaxMin, aes(x = MaxOffset)) + 
  geom_bar() + 
  scale_y_continuous(NULL, breaks = NULL) + 
  scale_x_discrete(limits = c(0:6), breaks = c(0:6)) + 
  labs(x = "Months") +
  ggtitle("Months Offset between NDVI and NDSSI")


ggplot(data= DeltaMaxMin) + 
  geom_bar(aes(x=MaxMeanNDVImonth),fill=3, alpha = 0.6) + 
  geom_bar(aes(x=MaxMeanNDSSImonth),fill=1, alpha = 0.6) +
  xlim(1,12) +
  labs(x = "NDVI") +
  ggtitle("Month of maximum mean NDSSI")

Ok, so the idea is that peak NDSSI is more effective if it occurs at moderate NDVI, so let’s look at the NDVI value for the months with peak NDSSI.

#extract NDVI value for each delta a the month of max NDSSI value

MaxNDSSI <- DeltaMaxMin %>%
  select(Delta,MaxMeanNDSSImonth) %>% 
  left_join(filter(DeltaMeans,surface == 'Land'), 
            by = c('Delta', 'MaxMeanNDSSImonth' ='month')) %>%
  select (-c(surface, MeanNDSSI)) %>%
  mutate(maxmonthNDVI = MeanNDVI) %>%
  select (-c(MeanNDVI,MaxMeanNDSSImonth))


#extract NDVI value for each delta at one month earlier than max NDSSI value

MaxNDSSIEarly <- DeltaMaxMin %>%
  select(Delta,MaxMeanNDSSImonth) %>% 
  mutate(EarlyNDSSI = if_else(MaxMeanNDSSImonth == 1, 12, MaxMeanNDSSImonth-1)) %>%
  select (-c(MaxMeanNDSSImonth)) %>%
  left_join(filter(DeltaMeans,surface == 'Land'), 
            by = c('Delta', 'EarlyNDSSI' ='month')) %>%
  select (-c(surface, MeanNDSSI)) %>%
  mutate(EarlyNDVI = MeanNDVI) %>%
  select (-c(MeanNDVI,EarlyNDSSI))

joinedPeakNDSSI <- left_join(MaxNDSSI, MaxNDSSIEarly, by = c('Delta'))

#remove intermediate data
rm(MaxNDSSI, MaxNDSSIEarly)

ggplot(data= joinedPeakNDSSI) + 
  geom_density(aes(x=maxmonthNDVI),fill=1, alpha = 0.6) + 
  geom_density(aes(x=EarlyNDVI),fill=3, alpha = 0.6) +
  xlim(0,1) +
  labs(x = "NDVI") +
  ggtitle("NDVI at month of maximum mean NDSSI and one month before")

Just to explore the data a bit, here are offsets against other measured parameters for each delta. The range, max and mean of NDVI and NDSSI is calculated from the timeseries, so it is really the max, min, and range of the monthly means (i.e., the maximum of the means, the minimum of the means, and the range of the mean). Offset is measured in months.

ggplot(data = select(DeltaMaxMin,rangeNDVI,MaxOffset,
                         rangeNDSSI,MaxMeanNDVI,MaxMeanNDSSI)) +
  geom_autopoint(na.rm = TRUE) +
  facet_matrix(rows = vars(rangeNDVI:MaxMeanNDSSI),
               switch = "x") + theme(aspect.ratio=1)
Adding missing grouping variables: `Delta`

Join Latitude and longitude data

DeltaDatawLocations <- left_join(select(DeltaMaxMin,rangeNDVI,MaxOffset,
                         rangeNDSSI,MaxMeanNDVI,MaxMeanNDSSI),
                         DeltaLocations, by = c("Delta" = "Deltas"))
Adding missing grouping variables: `Delta`
DeltaDatawLocations <- DeltaDatawLocations %>%
  mutate(Absolute_Latitude= abs(Lat)) 

ggplot(data = DeltaDatawLocations) +
  geom_autopoint(na.rm = TRUE) +
  facet_matrix(cols = vars(rangeNDVI:MaxMeanNDSSI), rows = vars(Absolute_Latitude) ,
               switch = "x") + theme(aspect.ratio=1)

So Offset and Lat seem to have a relationship:

#find the linear model 
DeltaOffset_lm <- lm( Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations) 

summary(DeltaOffset_lm)

Call:
lm(formula = Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations)

Residuals:
    Min      1Q  Median      3Q     Max 
-21.779  -8.983   0.775   8.377  20.772 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept)   10.820      4.629   2.337  0.02653 * 
MaxOffset      4.079      1.157   3.525  0.00143 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 11.06 on 29 degrees of freedom
Multiple R-squared:  0.2999,    Adjusted R-squared:  0.2758 
F-statistic: 12.43 on 1 and 29 DF,  p-value: 0.001428
ggplot(DeltaDatawLocations, aes(x = Absolute_Latitude, y = MaxOffset)) + 
  geom_point() +
  geom_smooth(mapping = aes(x = Absolute_Latitude, y = MaxOffset, ), method=lm ) 

Now for some maps of the data maps:

world <- ggplot() +
  borders("world", colour = "gray85", fill = "gray80") +
  theme_map() 

DeltaOffsetMap <- world +
  geom_point(aes(x = Lon, y = Lat, color = MaxOffset),
             data = DeltaDatawLocations, 
             size = 5) + scale_color_gradient( high = "red", low  = "yellow") +
  ggtitle("Offset Between NDVI peak on Land and NDSSI peak in water")

world <- ggplot() +
  borders("world", colour = "gray85", fill = "gray80") +
  theme_map() 

DeltaNDVIrangeMap <- world +
  geom_point(aes(x = Lon, y = Lat, color = rangeNDVI),
             data = DeltaDatawLocations,
             size = 5) + scale_color_gradient( high = "red", low  = "yellow") + 
  ggtitle("NDVI range")


DeltaNDSSIrangeMap  <- world +
  geom_point(aes(x = Lon, y = Lat, color = rangeNDSSI),
             data = DeltaDatawLocations, 
             size = 5) + scale_color_gradient( high = "red", low = "yellow") + ggtitle("NDSSI range") 


DeltaOffsetMap

DeltaNDVIrangeMap 

DeltaNDSSIrangeMap


#ggsave("DeltaOffsetMap.pdf", width = 6, height = 4)
#ggsave("DeltaNDVIrangeMap.pdf", width = 6, height = 4)
#ggsave("DeltaNDSSIrangeMap.pdf", width = 6, height = 4)

Let’s look at some of the timeseries To quantify the water, we use NDSSI. to quantify land, we use NDVI.

First here is the function to make the plots:

DeltaPlotter <- function(DeltaName) {
  #Counts each month
  numVeg <- DeltasCleaner %>%
    select(Delta, surface, month, ndvi) %>%
    filter(Delta == DeltaName & surface == "Land" & !is.na(ndvi)) %>%
    group_by(month) %>%
    summarize(n = n())
  
  numSed <- DeltasCleaner %>%
    select(Delta, surface, month, ndssi) %>%
    filter(Delta == DeltaName &
             surface == "Water" & !is.na(ndssi)) %>%
    group_by(month) %>%
    summarize(n = n())
  
  #Highlight the Maximum and Minimum Month for each delta, NDVI and NDSSI
  
  #LAND
  Veg <-
    ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
                           surface == "Land")) +
    geom_boxplot(aes(x = month, y = ndvi, group = month)) +
    scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
    expand_limits(x = c(1, 12)) +
    ggtitle(DeltaName) +
    #geom_text(data = numVeg, aes(y = 1.05, x = month, label = n)) +
    geom_boxplot(
      data = filter(
        DeltasCleaner,
        Delta == DeltaName &
          surface == "Land" & month == DeltaMaxMin$MaxMeanNDVImonth[DeltaMaxMin$Delta == DeltaName] 
      ),
      aes(x = month, y = ndvi, group = month),
      fill = "green"
    ) +
    geom_boxplot(
      data = filter(
        DeltasCleaner,
        Delta == DeltaName & 
          surface == "Land" & month == DeltaMaxMin$MinMeanNDVImonth[DeltaMaxMin$Delta ==DeltaName]
      ),
      aes(x = month, y = ndvi, group = month),
      fill = "blue"
    )
  
  
  Sed <-
    ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
                           surface == "Water")) +
    geom_boxplot(aes(x = month, y = ndssi, group = month)) +
    scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
    expand_limits(x = c(1, 12)) +
    #geom_text(data = numSed, aes(y = 1.05, x = month, label = n)) +
    geom_boxplot(
      data = filter(
        DeltasCleaner,
        Delta == DeltaName &
          surface == "Water" & month == DeltaMaxMin$MaxMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
      ),
      aes(x = month, y = ndssi, group = month),
      fill = "green"
    ) +
    geom_boxplot(
      data = filter(
        DeltasCleaner,
        Delta == DeltaName &
          surface == "Water" & month == DeltaMaxMin$MinMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
      ),
      aes(x = month, y = ndssi, group = month),
      fill = "blue"
    )
  
  return(grid.arrange(Veg, Sed, nrow = 2))
}

Here is are some examples:

DeltaPlotter("Parana")

DeltaPlotter("Magdalena")

DeltaPlotter("Ebro")

DeltaPlotter("Nile")

DeltaPlotter("Senegal")

DeltaPlotter("Orinoco")

DeltaPlotter("Godavari")

DeltaPlotter("Krishna")

And Finally, we can look at GRDC data:

#import the data (monthly means for 21 stations)
DeltasGRDC  <- read_csv("../data/GRDCstations.csv")
Parsed with column specification:
cols(
  Deltas = col_character(),
  GRDC_Station = col_double(),
  Time_Series_Length = col_character(),
  January = col_double(),
  February = col_double(),
  March = col_double(),
  April = col_double(),
  May = col_double(),
  June = col_double(),
  July = col_double(),
  August = col_double(),
  September = col_double(),
  October = col_double(),
  November = col_double(),
  December = col_double()
)
#calculate the mean of the monthly means
DeltasGRDC <- DeltasGRDC %>% 
    rowwise() %>% 
    mutate(MMD=mean(c(January,February,March,April,
                    May,June,July,August,
                    September,October,November,December)))  %>% 
  rowwise() %>% 
  mutate(Range_Discharge = max(c(January,February,March,April,
              May,June,July,August,
              September,October,November,December)) - 
           min(c(January,February,March,April,
              May,June,July,August,
              September,October,November,December))
           ) %>%
  right_join(DeltaDatawLocations, by = c("Deltas" = "Delta"))

#rename the months by numbers and tidy the GRDC data
DeltasDischarge <- DeltasGRDC %>%
  rename(Delta = Deltas,"1" = January, "2"= February, "3"= March, "4"= April,
         "5"=May, "6"=June, "7"=July, "8"= August, "9" = September, "10"=October, 
         "11"=November, "12"=December) %>%
  select(Delta, "1" , "2" , "3", "4","5", "6", "7", "8", "9", "10", "11", "12") %>%
  pivot_longer(-Delta, names_to = "month", values_to = "discharge")

DeltasDischarge$month = as.numeric(DeltasDischarge$month)

DisNDSSI <- DeltaMeans %>% 
  filter(surface == 'Water')  %>% 
  select (-c(MeanNDVI)) %>%
  left_join(DeltasDischarge, by = c('Delta', 'month')) %>%
  drop_na(discharge)

#plot monthly means against NDSSI for a given delta

ggplot(DisNDSSI, aes(y=discharge, x=MeanNDSSI)) + geom_point(shape=1) + facet_wrap( ~ Delta, ncol=4, , scales="free_y")

NA
NA
LS0tCnRpdGxlOiAiRGVsdGFzIE5vdGVib29rIDIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpMZXQncyBpbXBvcnQgYXQgYWxsIG9mIHRoZSBwYWNrYWdlcyBhbmQgdGhlIGRhdGEgKFJTIGFuZCBHZW9ncmFwaGljKToKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobWFwcykKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ2ZvcmNlKQoKRGVsdGFzQ2xlYW4gPC0gcmVhZF9jc3YoIi4uL2RhdGEvb3V0L2RlbHRhc19jbGVhbl92Mi5jc3YiKSAKRGVsdGFMb2NhdGlvbnMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvRGVsdGFMb2NhdGlvbnMuY3N2IikKYGBgCgpBcyBhIHJlbWluZGVyLCBmb3IgZWFjaCBvZiB0aGUgNDcgZGVsdGFzIHRoZXJlIGFyZSBtZWFzdXJlbWVudHMgb2YgTGFuZCAmIFdhdGVyIGFyZWFzIGF0IFVwc3RyZWFtLCBEb3duc3RyZWFtIGFuZCAnTWlkZGxlJyBsb2NhdGlvbnMgb24gdGhlIGRlbHRhLiBXZSBmaXJzdCBsdW1wIGFsbCB0aGUgb2JzZXJ2YXRpb25zIHRvZ2V0aGVyLCBhbmQgbG9vayB0byBzZWUgd2hpY2ggRGVsdGFzIGhhdmUgbWFueSBvYnNlcnZhdGlvbnM6CgpgYGB7cn0KI2NvdW50cyBwZXIgZGVsdGEKY291bnQoRGVsdGFzQ2xlYW4sIERlbHRhKQoKYGBgCgpOb3csIGJ5IGVhY2ggbW9udGguLiB3aGVyZSB0aGUgY29sb3JiYXIgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAobikgZm9yIGVhY2ggbW9udGggZm9yIGEgZ2l2ZW4gZGVsdGE6CmBgYHtyfQpnZ3Bsb3QoY291bnQoRGVsdGFzQ2xlYW4sIERlbHRhLCBtb250aCksIAogICAgICAgYWVzKHkgPSBEZWx0YSwgeCA9IG1vbnRoLCBmaWxsPW4pKSArIAogIGdlb21fdGlsZSgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLDEyKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50KCB0cmFucyA9ICdsb2cnICkKCmBgYAoKSW4gdGhlIGFib3ZlIGhlYXQgbWFwLCBkYXJrIGNvbG9ycyAoYW5kIG5vIGNvbG9yKSByZXByZXNlbnQgZGF0YSBwYXVjaXR5IChhbmQgZGF0YSBnYXBzKS4gRGVsdGFzIHdpdGggbGlnaHQgY29sb3JzIChlLmcuLCB0aGUgUGFyYW5hLCBOaWxlLCBFYnJvLCBDb2xvcmFkbywgQnJhaG1hbmkpIGhhdmUgbG90cyBvZiBkYXRhLCBzcHJlYWQgb3V0IHRocm91Z2ggdGhlIG1vbnRocyBvZiB0aGUgeWVhci4KCgpJJ2xsIHJlbW92ZS9zdWJzZXQgdGhlIGRlbHRhcyB3aXRoIHNwYXJzZSBjb3ZlcmFnZSAoc3BlY2lmaWNhbGx5LCBtb250aHMgd2l0aCBubyBjb3ZlcmFnZSkuLi4uIAoKYGBge3J9CgojIG5lZWQgMTAgZGF0YSBwb2ludHMgcGVyIG1vbnRoIGZvciBORFNTSSBhbmQgTkRWSQpFbm91Z2hPYnNQZXJNb250aCA8LSBEZWx0YXNDbGVhbiAlPiUgdW5ncm91cCgpICU+JQogIGNvdW50KERlbHRhLCBtb250aCwgc3VyZmFjZSkgJT4lIAogIGdyb3VwX2J5KHN1cmZhY2UpICU+JQogIGZpbHRlciggbiA+PSA1KQoKI2ZpbmQgZGVsdGFzIG1pc3NpbmcgYSBnaXZlbiBtb250aCBvZiBvYnNlcnZhdGlvbnMKRGVsdGFNb250aENvdW50cyA8LSBFbm91Z2hPYnNQZXJNb250aCAlPiUKICB1bmdyb3VwKCkgJT4lCiAgY291bnQoRGVsdGEpCgojIG5lZWQgMTIgbW9udGhzIG9mIHdhdGVyIGFuZCBsYW5kIG9icywgc28gMjQgbW8gdG90YWwKRW5vdWdoTW9udGhzIDwtIERlbHRhTW9udGhDb3VudHMgJT4lCiBmaWx0ZXIoIG4gPT0gMjQpCgpDb21wbGV0ZU9ic0RlbHRhcyA8LSBwdWxsKEVub3VnaE1vbnRocywgRGVsdGEpCgojcmVtb3ZlIHRoZW0KRGVsdGFzQ2xlYW5lciA8LSBEZWx0YXNDbGVhbiAlPiUKICBmaWx0ZXIoRGVsdGEgJWluJSBDb21wbGV0ZU9ic0RlbHRhcykKCiNhZGQgdGhlIHJlYWwgZGF0ZXMgaW4gbW9udGggZGF0ZSBmb3JtYXQKRGVsdGFzQ2xlYW5lciRkYXRlIDwtIGFzLkRhdGUocGFzdGUoRGVsdGFzQ2xlYW5lciR5ZWFyLCBEZWx0YXNDbGVhbmVyJG1vbnRoLCAiMDEiLCBzZXA9Ii0iKSwgIiVZLSVtLSVkIikKCiNyZW1vdmUgaW50ZXJtZWRpYXRlIGRhdGEKcm0oQ29tcGxldGVPYnNEZWx0YXMsIEVub3VnaE1vbnRocywgRW5vdWdoT2JzUGVyTW9udGgsIERlbHRhTW9udGhDb3VudHMsIERlbHRhc0NsZWFuKQoKYGBgCgpPaywgbm93IGxldCdzIG9yZ2FuaXplIHRoaXMgcmF3IGRhdGEgaW50byAxIGRhdGFmcmFtZSwgd2l0aCBkZWx0YSwgbWF4IGFuZCBtaW4gTkRWSSAoYW5kIG1vbnRoKSwgbWF4IGFuZCBtaW4gTkRTU0koYW5kIG1vbnRoKSwgTkRWSSBhbmQgTkRTU0kgcmFuZ2UuCgpgYGB7ciAgaW5jbHVkZSA9IFRSVUV9CiN0YWtlIHRoZSBtZWFuIE5EVkkgYW5kIE5EU1NJIGZvciBlYWNoIG1vbnRoLCBmb3IgZWFjaCBkZWx0YQpEZWx0YU1lYW5zIDwtIERlbHRhc0NsZWFuZXIgJT4lCiAgZ3JvdXBfYnkoRGVsdGEsIG1vbnRoLCBzdXJmYWNlKSAlPiUKICBzdW1tYXJpemUoTWVhbk5EVkkgPSBtZWFuKG5kdmksIG5hLnJtID0gVFJVRSksIE1lYW5ORFNTSSA9IG1lYW4obmRzc2ksIG5hLnJtID0gVFJVRSkpCgojIyMjIwoKRGVsdGFNYXhORFZJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdMYW5kJykgICU+JSAKICBzZWxlY3QgKC1jKE1lYW5ORFNTSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1heChNZWFuTkRWSSkpICU+JSAKICByZW5hbWUoTWF4TWVhbk5EVkltb250aCA9IG1vbnRoLCBNYXhNZWFuTkRWSSA9IE1lYW5ORFZJKQoKRGVsdGFNYXhORFNTSSA8LSAKICBEZWx0YU1lYW5zICU+JSAKICBmaWx0ZXIoc3VyZmFjZSA9PSAnV2F0ZXInKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EVkkpKSAlPiUgCiAgZ3JvdXBfYnkoRGVsdGEpICU+JSAKICBzbGljZSh3aGljaC5tYXgoTWVhbk5EU1NJKSkgJT4lIAogIHJlbmFtZShNYXhNZWFuTkRTU0ltb250aCA9IG1vbnRoLCBNYXhNZWFuTkRTU0kgPSBNZWFuTkRTU0kpCgpEZWx0YU1pbk5EVkkgPC0gCiAgRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ0xhbmQnKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EU1NJKSkgJT4lIAogIGdyb3VwX2J5KERlbHRhKSAlPiUgCiAgc2xpY2Uod2hpY2gubWluKE1lYW5ORFZJKSkgJT4lIAogIHJlbmFtZShNaW5NZWFuTkRWSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFZJID0gTWVhbk5EVkkpCgpEZWx0YU1pbk5EU1NJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdXYXRlcicpICAlPiUgCiAgc2VsZWN0ICgtYyhNZWFuTkRWSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1pbihNZWFuTkRTU0kpKSAlPiUgCiAgcmVuYW1lKE1pbk1lYW5ORFNTSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFNTSSA9IE1lYW5ORFNTSSkKCgojam9pbiBpbnRvIDEgZGF0YWZyYW1lCkRlbHRhTWF4TWluIDwtIGxlZnRfam9pbihEZWx0YU1heE5EVkksIERlbHRhTWF4TkRTU0ksIGJ5ID0gJ0RlbHRhJykgJT4lIAogIGxlZnRfam9pbiguLERlbHRhTWluTkRWSSwgYnkgPSAnRGVsdGEnKSAlPiUgCiAgbGVmdF9qb2luKC4sRGVsdGFNaW5ORFNTSSwgYnkgPSAnRGVsdGEnKSAKCiNyZW1vdmUgaW50ZXJtZWRpYXRlIGRhdGEKcm0oRGVsdGFNYXhORFZJLCBEZWx0YU1heE5EU1NJLCBEZWx0YU1pbk5EU1NJLERlbHRhTWluTkRWSSkKCkRlbHRhTWF4TWluIDwtIERlbHRhTWF4TWluICU+JQogIHNlbGVjdCgtc3VyZmFjZS54LCAtc3VyZmFjZS54LngsIC1zdXJmYWNlLnksIC1zdXJmYWNlLnkueSkKCmBgYApBbmQgbm93IHdlIGNhbGN1bGF0ZSBwaGFzZSBzaGlmdHMgYmV0d2VlbiBORFZJIGFuZCBORFNTSSBmb3IgZWFjaCBkZWx0YQpgYGB7ciB9CiNjb21wYXJlIG9mZnNldApEZWx0YU1heE1pbiA8LSBtdXRhdGUoRGVsdGFNYXhNaW4sIAogICAgICAgICAgICAgICAgICAgICAgTWluT2Zmc2V0ID0gaWZfZWxzZShNaW5NZWFuTkRWSW1vbnRoID4gTWluTWVhbk5EU1NJbW9udGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNaW5NZWFuTkRWSW1vbnRoIC0gTWluTWVhbk5EU1NJbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1pbk1lYW5ORFNTSW1vbnRoIC0gTWluTWVhbk5EVkltb250aCksCiAgICAgICAgICAgICAgICAgICAgICBNYXhPZmZzZXQgPSBpZl9lbHNlKE1heE1lYW5ORFZJbW9udGggPiBNYXhNZWFuTkRTU0ltb250aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1heE1lYW5ORFZJbW9udGggLSBNYXhNZWFuTkRTU0ltb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF4TWVhbk5EU1NJbW9udGggLSBNYXhNZWFuTkRWSW1vbnRoKSwKICAgICAgICAgICAgICAgICAgICAgIE9mZnNldERpZmYgPSBhYnMoTWF4T2Zmc2V0IC0gTWluT2Zmc2V0KSwKICAgICAgICAgICAgICAgICAgICAgIHJhbmdlTkRWSSA9IChNYXhNZWFuTkRWSSAtIE1pbk1lYW5ORFZJKSwgCiAgICAgICAgICAgICAgICAgICAgICByYW5nZU5EU1NJID0gKE1heE1lYW5ORFNTSSAtIE1pbk1lYW5ORFNTSSkKICAgICAgICAgICAgICAgICAgICAgICkKCkRlbHRhTWF4TWluIDwtIERlbHRhTWF4TWluICU+JSAKICBtdXRhdGUoTWluT2Zmc2V0ID0gaWZlbHNlKE1pbk9mZnNldCA+IDYsICgtMSpNaW5PZmZzZXQpKzEyICxNaW5PZmZzZXQpKQoKRGVsdGFNYXhNaW4gPC0gRGVsdGFNYXhNaW4gJT4lIAogIG11dGF0ZShNYXhPZmZzZXQgPSBpZmVsc2UoTWF4T2Zmc2V0ID4gNiwgKC0xKk1heE9mZnNldCkrMTIgLE1heE9mZnNldCkpICAgICAgICAKICAKCgpEZWx0YU1heE1pbgoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IERlbHRhLCB4ID0gTWF4T2Zmc2V0KSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoMTo2KSwgYnJlYWtzID0gYygxOjYpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYygwLDYpKSAgKyAKICBnZ3RpdGxlKCJNYXhPZmZzZXQiKQoKIyBnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh5ID0gRGVsdGEsIHggPSBNaW5PZmZzZXQpKSArIGdlb21fcG9pbnQoKSArIAojICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6NiksIGJyZWFrcyA9IGMoMTo2KSkgKwojICAgZXhwYW5kX2xpbWl0cyh4ID0gYygwLDYpKSAgKyAKIyAgIGdndGl0bGUoIk1pbk9mZnNldCIpCiMgCiMgZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IERlbHRhLCB4ID0gT2Zmc2V0RGlmZikpICsgZ2VvbV9wb2ludCgpICsgCiMgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoMTo2KSwgYnJlYWtzID0gYygxOjYpKSArCiMgICBleHBhbmRfbGltaXRzKHggPSBjKDAsNikpICArIAojICAgZ2d0aXRsZSgiT2Zmc2V0IERpZmZlcmVuY2UiKQoKYGBgCgpOb3cgbGV0J3MgZXhhbWluZSB0aGUgaGlzdG9ncmFtcyBvZiBhbGwgMzEgZGVsdGFzLi4uIFRoZSBtb250aHMgd2l0aCB0aGUgZ3JlYXRlc3QgbWVhbiBORFZJLCBtb250aHMgd2l0aCBncmV0YWVzdCBtZWFuIE5EU1NJLCB0aGUgbW9udGhseSBvZmZzZXQsIGFuZCB0aGUgc2tldyBvZiB0aGUgTkRTU0kgYW5kIE5EVkkgdGltZXNlcmllcy4KCmBgYHtyfQpnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh4ID0gTWF4TWVhbk5EVkltb250aCkpICsgCiAgZ2VvbV9iYXIoZmlsbD0zKSArIAogIHNjYWxlX3lfY29udGludW91cyhOVUxMLCBicmVha3MgPSBOVUxMKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKyAKICBsYWJzKHggPSAiTW9udGgiKSArCiAgZ2d0aXRsZSgiTW9udGggb2YgbWF4aW11bSBtZWFuIE5EVkkiKQoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeCA9IE1heE1lYW5ORFNTSW1vbnRoKSkgKyAKICBnZW9tX2JhcigpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKE5VTEwsIGJyZWFrcyA9IE5VTEwpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArIAogIGxhYnMoeCA9ICJNb250aCIpICsKICBnZ3RpdGxlKCJNb250aCBvZiBtYXhpbXVtIG1lYW4gTkRTU0kiKQoKZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeCA9IE1heE9mZnNldCkpICsgCiAgZ2VvbV9iYXIoKSArIAogIHNjYWxlX3lfY29udGludW91cyhOVUxMLCBicmVha3MgPSBOVUxMKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygwOjYpLCBicmVha3MgPSBjKDA6NikpICsgCiAgbGFicyh4ID0gIk1vbnRocyIpICsKICBnZ3RpdGxlKCJNb250aHMgT2Zmc2V0IGJldHdlZW4gTkRWSSBhbmQgTkRTU0kiKQoKZ2dwbG90KGRhdGE9IERlbHRhTWF4TWluKSArIAogIGdlb21fYmFyKGFlcyh4PU1heE1lYW5ORFZJbW9udGgpLGZpbGw9MywgYWxwaGEgPSAwLjYpICsgCiAgZ2VvbV9iYXIoYWVzKHg9TWF4TWVhbk5EU1NJbW9udGgpLGZpbGw9MSwgYWxwaGEgPSAwLjYpICsKICB4bGltKDEsMTIpICsKICBsYWJzKHggPSAiTkRWSSIpICsKICBnZ3RpdGxlKCJNb250aCBvZiBtYXhpbXVtIG1lYW4gTkRTU0kiKQoKYGBgCgpPaywgc28gdGhlIGlkZWEgaXMgdGhhdCBwZWFrIE5EU1NJIGlzIG1vcmUgZWZmZWN0aXZlIGlmIGl0IG9jY3VycyBhdCBtb2RlcmF0ZSBORFZJLCBzbyBsZXQncyBsb29rIGF0IHRoZSBORFZJIHZhbHVlIGZvciB0aGUgbW9udGhzIHdpdGggcGVhayBORFNTSS4gCmBgYHtyfQojZXh0cmFjdCBORFZJIHZhbHVlIGZvciBlYWNoIGRlbHRhIGEgdGhlIG1vbnRoIG9mIG1heCBORFNTSSB2YWx1ZQoKTWF4TkRTU0kgPC0gRGVsdGFNYXhNaW4gJT4lCiAgc2VsZWN0KERlbHRhLE1heE1lYW5ORFNTSW1vbnRoKSAlPiUgCiAgbGVmdF9qb2luKGZpbHRlcihEZWx0YU1lYW5zLHN1cmZhY2UgPT0gJ0xhbmQnKSwgCiAgICAgICAgICAgIGJ5ID0gYygnRGVsdGEnLCAnTWF4TWVhbk5EU1NJbW9udGgnID0nbW9udGgnKSkgJT4lCiAgc2VsZWN0ICgtYyhzdXJmYWNlLCBNZWFuTkRTU0kpKSAlPiUKICBtdXRhdGUobWF4bW9udGhORFZJID0gTWVhbk5EVkkpICU+JQogIHNlbGVjdCAoLWMoTWVhbk5EVkksTWF4TWVhbk5EU1NJbW9udGgpKQoKCiNleHRyYWN0IE5EVkkgdmFsdWUgZm9yIGVhY2ggZGVsdGEgYXQgb25lIG1vbnRoIGVhcmxpZXIgdGhhbiBtYXggTkRTU0kgdmFsdWUKCk1heE5EU1NJRWFybHkgPC0gRGVsdGFNYXhNaW4gJT4lCiAgc2VsZWN0KERlbHRhLE1heE1lYW5ORFNTSW1vbnRoKSAlPiUgCiAgbXV0YXRlKEVhcmx5TkRTU0kgPSBpZl9lbHNlKE1heE1lYW5ORFNTSW1vbnRoID09IDEsIDEyLCBNYXhNZWFuTkRTU0ltb250aC0xKSkgJT4lCiAgc2VsZWN0ICgtYyhNYXhNZWFuTkRTU0ltb250aCkpICU+JQogIGxlZnRfam9pbihmaWx0ZXIoRGVsdGFNZWFucyxzdXJmYWNlID09ICdMYW5kJyksIAogICAgICAgICAgICBieSA9IGMoJ0RlbHRhJywgJ0Vhcmx5TkRTU0knID0nbW9udGgnKSkgJT4lCiAgc2VsZWN0ICgtYyhzdXJmYWNlLCBNZWFuTkRTU0kpKSAlPiUKICBtdXRhdGUoRWFybHlORFZJID0gTWVhbk5EVkkpICU+JQogIHNlbGVjdCAoLWMoTWVhbk5EVkksRWFybHlORFNTSSkpCgpqb2luZWRQZWFrTkRTU0kgPC0gbGVmdF9qb2luKE1heE5EU1NJLCBNYXhORFNTSUVhcmx5LCBieSA9IGMoJ0RlbHRhJykpCgojcmVtb3ZlIGludGVybWVkaWF0ZSBkYXRhCnJtKE1heE5EU1NJLCBNYXhORFNTSUVhcmx5KQoKZ2dwbG90KGRhdGE9IGpvaW5lZFBlYWtORFNTSSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHg9bWF4bW9udGhORFZJKSxmaWxsPTEsIGFscGhhID0gMC42KSArIAogIGdlb21fZGVuc2l0eShhZXMoeD1FYXJseU5EVkkpLGZpbGw9MywgYWxwaGEgPSAwLjYpICsKICB4bGltKDAsMSkgKwogIGxhYnMoeCA9ICJORFZJIikgKwogIGdndGl0bGUoIk5EVkkgYXQgbW9udGggb2YgbWF4aW11bSBtZWFuIE5EU1NJIGFuZCBvbmUgbW9udGggYmVmb3JlIikKCmBgYAoKSnVzdCB0byBleHBsb3JlIHRoZSBkYXRhIGEgYml0LCBoZXJlIGFyZSBvZmZzZXRzIGFnYWluc3Qgb3RoZXIgbWVhc3VyZWQgcGFyYW1ldGVycyBmb3IgZWFjaCBkZWx0YS4gVGhlIHJhbmdlLCBtYXggYW5kIG1lYW4gb2YgTkRWSSBhbmQgTkRTU0kgaXMgY2FsY3VsYXRlZCBmcm9tIHRoZSB0aW1lc2VyaWVzLCBzbyBpdCBpcyByZWFsbHkgdGhlIG1heCwgbWluLCBhbmQgcmFuZ2Ugb2YgdGhlIG1vbnRobHkgbWVhbnMgKGkuZS4sIHRoZSBtYXhpbXVtIG9mIHRoZSBtZWFucywgdGhlIG1pbmltdW0gb2YgdGhlIG1lYW5zLCBhbmQgdGhlIHJhbmdlIG9mIHRoZSBtZWFuKS4gT2Zmc2V0IGlzIG1lYXN1cmVkIGluIG1vbnRocy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNlbGVjdChEZWx0YU1heE1pbixyYW5nZU5EVkksTWF4T2Zmc2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2VORFNTSSxNYXhNZWFuTkRWSSxNYXhNZWFuTkRTU0kpKSArCiAgZ2VvbV9hdXRvcG9pbnQobmEucm0gPSBUUlVFKSArCiAgZmFjZXRfbWF0cml4KHJvd3MgPSB2YXJzKHJhbmdlTkRWSTpNYXhNZWFuTkRTU0kpLAogICAgICAgICAgICAgICBzd2l0Y2ggPSAieCIpICsgdGhlbWUoYXNwZWN0LnJhdGlvPTEpCgpgYGAKCkpvaW4gTGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBkYXRhCgpgYGB7cn0KRGVsdGFEYXRhd0xvY2F0aW9ucyA8LSBsZWZ0X2pvaW4oc2VsZWN0KERlbHRhTWF4TWluLHJhbmdlTkRWSSxNYXhPZmZzZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZU5EU1NJLE1heE1lYW5ORFZJLE1heE1lYW5ORFNTSSksCiAgICAgICAgICAgICAgICAgICAgICAgICBEZWx0YUxvY2F0aW9ucywgYnkgPSBjKCJEZWx0YSIgPSAiRGVsdGFzIikpCgpEZWx0YURhdGF3TG9jYXRpb25zIDwtIERlbHRhRGF0YXdMb2NhdGlvbnMgJT4lCiAgbXV0YXRlKEFic29sdXRlX0xhdGl0dWRlPSBhYnMoTGF0KSkgCgpnZ3Bsb3QoZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMpICsKICBnZW9tX2F1dG9wb2ludChuYS5ybSA9IFRSVUUpICsKICBmYWNldF9tYXRyaXgoY29scyA9IHZhcnMocmFuZ2VORFZJOk1heE1lYW5ORFNTSSksIHJvd3MgPSB2YXJzKEFic29sdXRlX0xhdGl0dWRlKSAsCiAgICAgICAgICAgICAgIHN3aXRjaCA9ICJ4IikgKyB0aGVtZShhc3BlY3QucmF0aW89MSkKYGBgCgoKU28gT2Zmc2V0IGFuZCBMYXQgc2VlbSB0byBoYXZlIGEgcmVsYXRpb25zaGlwOgoKCmBgYHtyfQojZmluZCB0aGUgbGluZWFyIG1vZGVsIApEZWx0YU9mZnNldF9sbSA8LSBsbSggQWJzb2x1dGVfTGF0aXR1ZGUgfiBNYXhPZmZzZXQsIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zKSAKCnN1bW1hcnkoRGVsdGFPZmZzZXRfbG0pCgpnZ3Bsb3QoRGVsdGFEYXRhd0xvY2F0aW9ucywgYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCwgKSwgbWV0aG9kPWxtICkgCgpgYGAKCk5vdyBmb3Igc29tZSBtYXBzIG9mIHRoZSBkYXRhIG1hcHM6CgpgYGB7cn0Kd29ybGQgPC0gZ2dwbG90KCkgKwogIGJvcmRlcnMoIndvcmxkIiwgY29sb3VyID0gImdyYXk4NSIsIGZpbGwgPSAiZ3JheTgwIikgKwogIHRoZW1lX21hcCgpIAoKRGVsdGFPZmZzZXRNYXAgPC0gd29ybGQgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb24sIHkgPSBMYXQsIGNvbG9yID0gTWF4T2Zmc2V0KSwKICAgICAgICAgICAgIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zLCAKICAgICAgICAgICAgIHNpemUgPSA1KSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KCBoaWdoID0gInJlZCIsIGxvdyAgPSAieWVsbG93IikgKwogIGdndGl0bGUoIk9mZnNldCBCZXR3ZWVuIE5EVkkgcGVhayBvbiBMYW5kIGFuZCBORFNTSSBwZWFrIGluIHdhdGVyIikKCndvcmxkIDwtIGdncGxvdCgpICsKICBib3JkZXJzKCJ3b3JsZCIsIGNvbG91ciA9ICJncmF5ODUiLCBmaWxsID0gImdyYXk4MCIpICsKICB0aGVtZV9tYXAoKSAKCkRlbHRhTkRWSXJhbmdlTWFwIDwtIHdvcmxkICsKICBnZW9tX3BvaW50KGFlcyh4ID0gTG9uLCB5ID0gTGF0LCBjb2xvciA9IHJhbmdlTkRWSSksCiAgICAgICAgICAgICBkYXRhID0gRGVsdGFEYXRhd0xvY2F0aW9ucywKICAgICAgICAgICAgIHNpemUgPSA1KSArIHNjYWxlX2NvbG9yX2dyYWRpZW50KCBoaWdoID0gInJlZCIsIGxvdyAgPSAieWVsbG93IikgKyAKICBnZ3RpdGxlKCJORFZJIHJhbmdlIikKCgpEZWx0YU5EU1NJcmFuZ2VNYXAgIDwtIHdvcmxkICsKICBnZW9tX3BvaW50KGFlcyh4ID0gTG9uLCB5ID0gTGF0LCBjb2xvciA9IHJhbmdlTkRTU0kpLAogICAgICAgICAgICAgZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMsIAogICAgICAgICAgICAgc2l6ZSA9IDUpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQoIGhpZ2ggPSAicmVkIiwgbG93ID0gInllbGxvdyIpICsgZ2d0aXRsZSgiTkRTU0kgcmFuZ2UiKSAKCgpEZWx0YU9mZnNldE1hcApEZWx0YU5EVklyYW5nZU1hcCAKRGVsdGFORFNTSXJhbmdlTWFwCgojZ2dzYXZlKCJEZWx0YU9mZnNldE1hcC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCiNnZ3NhdmUoIkRlbHRhTkRWSXJhbmdlTWFwLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKI2dnc2F2ZSgiRGVsdGFORFNTSXJhbmdlTWFwLnBkZiIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKCmBgYAoKTGV0J3MgbG9vayBhdCBzb21lIG9mIHRoZSB0aW1lc2VyaWVzIApUbyBxdWFudGlmeSB0aGUgd2F0ZXIsIHdlIHVzZSBORFNTSS4gdG8gcXVhbnRpZnkgbGFuZCwgd2UgdXNlIE5EVkkuCgpGaXJzdCBoZXJlIGlzIHRoZSBmdW5jdGlvbiB0byBtYWtlIHRoZSBwbG90czoKCmBgYHtyfQpEZWx0YVBsb3R0ZXIgPC0gZnVuY3Rpb24oRGVsdGFOYW1lKSB7CiAgI0NvdW50cyBlYWNoIG1vbnRoCiAgbnVtVmVnIDwtIERlbHRhc0NsZWFuZXIgJT4lCiAgICBzZWxlY3QoRGVsdGEsIHN1cmZhY2UsIG1vbnRoLCBuZHZpKSAlPiUKICAgIGZpbHRlcihEZWx0YSA9PSBEZWx0YU5hbWUgJiBzdXJmYWNlID09ICJMYW5kIiAmICFpcy5uYShuZHZpKSkgJT4lCiAgICBncm91cF9ieShtb250aCkgJT4lCiAgICBzdW1tYXJpemUobiA9IG4oKSkKICAKICBudW1TZWQgPC0gRGVsdGFzQ2xlYW5lciAlPiUKICAgIHNlbGVjdChEZWx0YSwgc3VyZmFjZSwgbW9udGgsIG5kc3NpKSAlPiUKICAgIGZpbHRlcihEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgICAgc3VyZmFjZSA9PSAiV2F0ZXIiICYgIWlzLm5hKG5kc3NpKSkgJT4lCiAgICBncm91cF9ieShtb250aCkgJT4lCiAgICBzdW1tYXJpemUobiA9IG4oKSkKICAKICAjSGlnaGxpZ2h0IHRoZSBNYXhpbXVtIGFuZCBNaW5pbXVtIE1vbnRoIGZvciBlYWNoIGRlbHRhLCBORFZJIGFuZCBORFNTSQogIAogICNMQU5ECiAgVmVnIDwtCiAgICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcihEZWx0YXNDbGVhbmVyLCBEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJmYWNlID09ICJMYW5kIikpICsKICAgIGdlb21fYm94cGxvdChhZXMoeCA9IG1vbnRoLCB5ID0gbmR2aSwgZ3JvdXAgPSBtb250aCkpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLCAxMikpICsKICAgIGdndGl0bGUoRGVsdGFOYW1lKSArCiAgICAjZ2VvbV90ZXh0KGRhdGEgPSBudW1WZWcsIGFlcyh5ID0gMS4wNSwgeCA9IG1vbnRoLCBsYWJlbCA9IG4pKSArCiAgICBnZW9tX2JveHBsb3QoCiAgICAgIGRhdGEgPSBmaWx0ZXIoCiAgICAgICAgRGVsdGFzQ2xlYW5lciwKICAgICAgICBEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgc3VyZmFjZSA9PSAiTGFuZCIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNYXhNZWFuTkRWSW1vbnRoW0RlbHRhTWF4TWluJERlbHRhID09IERlbHRhTmFtZV0gCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHZpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJncmVlbiIKICAgICkgKwogICAgZ2VvbV9ib3hwbG90KAogICAgICBkYXRhID0gZmlsdGVyKAogICAgICAgIERlbHRhc0NsZWFuZXIsCiAgICAgICAgRGVsdGEgPT0gRGVsdGFOYW1lICYgCiAgICAgICAgICBzdXJmYWNlID09ICJMYW5kIiAmIG1vbnRoID09IERlbHRhTWF4TWluJE1pbk1lYW5ORFZJbW9udGhbRGVsdGFNYXhNaW4kRGVsdGEgPT1EZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHZpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJibHVlIgogICAgKQogIAogIAogIFNlZCA8LQogICAgZ2dwbG90KGRhdGEgPSBmaWx0ZXIoRGVsdGFzQ2xlYW5lciwgRGVsdGEgPT0gRGVsdGFOYW1lICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VyZmFjZSA9PSAiV2F0ZXIiKSkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCkpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjEyKSwgYnJlYWtzID0gYygxOjEyKSkgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLCAxMikpICsKICAgICNnZW9tX3RleHQoZGF0YSA9IG51bVNlZCwgYWVzKHkgPSAxLjA1LCB4ID0gbW9udGgsIGxhYmVsID0gbikpICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgZGF0YSA9IGZpbHRlcigKICAgICAgICBEZWx0YXNDbGVhbmVyLAogICAgICAgIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNYXhNZWFuTkRTU0ltb250aFtEZWx0YU1heE1pbiREZWx0YSA9PSBEZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCksCiAgICAgIGZpbGwgPSAiZ3JlZW4iCiAgICApICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgZGF0YSA9IGZpbHRlcigKICAgICAgICBEZWx0YXNDbGVhbmVyLAogICAgICAgIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIgJiBtb250aCA9PSBEZWx0YU1heE1pbiRNaW5NZWFuTkRTU0ltb250aFtEZWx0YU1heE1pbiREZWx0YSA9PSBEZWx0YU5hbWVdCiAgICAgICksCiAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBuZHNzaSwgZ3JvdXAgPSBtb250aCksCiAgICAgIGZpbGwgPSAiYmx1ZSIKICAgICkKICAKICByZXR1cm4oZ3JpZC5hcnJhbmdlKFZlZywgU2VkLCBucm93ID0gMikpCn0KYGBgCgoKSGVyZSBpcyBhcmUgc29tZSBleGFtcGxlczoKCiogVGhlIHBlYWtzIGluIGJvdGggdGltZXNlcmllcyBzaGlmdCBhcm91bmQgZGVwZW5kaW5nIG9uIHRoZSBkZWx0YToKICAgKyBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBpbiB0aGUgT3Jpbm9jbyBuYWQgU2VuZWdhbAogICArIFRoZSBhbnRpY29ycmVsYXRpb24gaW4gdGhlIFBhcmFuYSBhbmQgRWJybywKICAgKyBUaGUgc2xpZ2h0IHBoYXNlIHNoaWZ0IGluIHRoZSBNYWdkYWxlbmEuCgpgYGB7cn0KRGVsdGFQbG90dGVyKCJQYXJhbmEiKQpEZWx0YVBsb3R0ZXIoIk1hZ2RhbGVuYSIpCkRlbHRhUGxvdHRlcigiRWJybyIpCkRlbHRhUGxvdHRlcigiTmlsZSIpCkRlbHRhUGxvdHRlcigiU2VuZWdhbCIpCkRlbHRhUGxvdHRlcigiT3Jpbm9jbyIpCkRlbHRhUGxvdHRlcigiR29kYXZhcmkiKQpEZWx0YVBsb3R0ZXIoIktyaXNobmEiKQpgYGAKCkFuZCBGaW5hbGx5LCB3ZSBjYW4gbG9vayBhdCBHUkRDIGRhdGE6CmBgYHtyfQojaW1wb3J0IHRoZSBkYXRhIChtb250aGx5IG1lYW5zIGZvciAyMSBzdGF0aW9ucykKRGVsdGFzR1JEQyAgPC0gcmVhZF9jc3YoIi4uL2RhdGEvR1JEQ3N0YXRpb25zLmNzdiIpCgojY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSBtb250aGx5IG1lYW5zCkRlbHRhc0dSREMgPC0gRGVsdGFzR1JEQyAlPiUgCiAgICByb3d3aXNlKCkgJT4lIAogICAgbXV0YXRlKE1NRD1tZWFuKGMoSmFudWFyeSxGZWJydWFyeSxNYXJjaCxBcHJpbCwKICAgICAgICAgICAgICAgICAgICBNYXksSnVuZSxKdWx5LEF1Z3VzdCwKICAgICAgICAgICAgICAgICAgICBTZXB0ZW1iZXIsT2N0b2JlcixOb3ZlbWJlcixEZWNlbWJlcikpKSAgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgbXV0YXRlKFJhbmdlX0Rpc2NoYXJnZSA9IG1heChjKEphbnVhcnksRmVicnVhcnksTWFyY2gsQXByaWwsCiAgICAgICAgICAgICAgTWF5LEp1bmUsSnVseSxBdWd1c3QsCiAgICAgICAgICAgICAgU2VwdGVtYmVyLE9jdG9iZXIsTm92ZW1iZXIsRGVjZW1iZXIpKSAtIAogICAgICAgICAgIG1pbihjKEphbnVhcnksRmVicnVhcnksTWFyY2gsQXByaWwsCiAgICAgICAgICAgICAgTWF5LEp1bmUsSnVseSxBdWd1c3QsCiAgICAgICAgICAgICAgU2VwdGVtYmVyLE9jdG9iZXIsTm92ZW1iZXIsRGVjZW1iZXIpKQogICAgICAgICAgICkgJT4lCiAgcmlnaHRfam9pbihEZWx0YURhdGF3TG9jYXRpb25zLCBieSA9IGMoIkRlbHRhcyIgPSAiRGVsdGEiKSkKCiNyZW5hbWUgdGhlIG1vbnRocyBieSBudW1iZXJzIGFuZCB0aWR5IHRoZSBHUkRDIGRhdGEKRGVsdGFzRGlzY2hhcmdlIDwtIERlbHRhc0dSREMgJT4lCiAgcmVuYW1lKERlbHRhID0gRGVsdGFzLCIxIiA9IEphbnVhcnksICIyIj0gRmVicnVhcnksICIzIj0gTWFyY2gsICI0Ij0gQXByaWwsCiAgICAgICAgICI1Ij1NYXksICI2Ij1KdW5lLCAiNyI9SnVseSwgIjgiPSBBdWd1c3QsICI5IiA9IFNlcHRlbWJlciwgIjEwIj1PY3RvYmVyLCAKICAgICAgICAgIjExIj1Ob3ZlbWJlciwgIjEyIj1EZWNlbWJlcikgJT4lCiAgc2VsZWN0KERlbHRhLCAiMSIgLCAiMiIgLCAiMyIsICI0IiwiNSIsICI2IiwgIjciLCAiOCIsICI5IiwgIjEwIiwgIjExIiwgIjEyIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1EZWx0YSwgbmFtZXNfdG8gPSAibW9udGgiLCB2YWx1ZXNfdG8gPSAiZGlzY2hhcmdlIikKCkRlbHRhc0Rpc2NoYXJnZSRtb250aCA9IGFzLm51bWVyaWMoRGVsdGFzRGlzY2hhcmdlJG1vbnRoKQoKRGlzTkRTU0kgPC0gRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ1dhdGVyJykgICU+JSAKICBzZWxlY3QgKC1jKE1lYW5ORFZJKSkgJT4lCiAgbGVmdF9qb2luKERlbHRhc0Rpc2NoYXJnZSwgYnkgPSBjKCdEZWx0YScsICdtb250aCcpKSAlPiUKICBkcm9wX25hKGRpc2NoYXJnZSkKCiNwbG90IG1vbnRobHkgbWVhbnMgYWdhaW5zdCBORFNTSSBmb3IgZGVsdGFzIHdpdGggR1JEQyBkaXNjaGFyZ2UgZGF0YQpnZ3Bsb3QoRGlzTkRTU0ksIGFlcyh5PWRpc2NoYXJnZSwgeD1NZWFuTkRTU0kpKSArIGdlb21fcG9pbnQoc2hhcGU9MSkgKyBmYWNldF93cmFwKCB+IERlbHRhLCBuY29sPTQsICwgc2NhbGVzPSJmcmVlX3kiKQoKYGBgCgo=